package com.server.questionbank.snframe.controller.Note;

import com.google.common.net.HttpHeaders;
import com.server.questionbank.snframe.domain.Note.UserAttachment;
import com.server.questionbank.snframe.service.NoteService.UserAttachmentService;
import com.server.questionbank.snframe.util.FileUtil.EmptyMultipartFile;
import com.server.questionbank.snframe.util.FileUtil.MinIoUtil;
import io.minio.GetObjectResponse;
import io.minio.StatObjectResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Set;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;

@Slf4j
@RestController
@RequestMapping("api/minio")
@Api(tags = {"MinIO文件操作接口"})
public class MinIoController {

    /**
     * 允许上传的文件Content-Type类型
     */
    private static final Set<String> ALLOWED_CONTENT_TYPES = new HashSet<>(Arrays.asList(
            // 图片类型
            "image/jpeg", "image/png", "image/gif", "image/bmp", "image/webp",
            "image/tiff", "image/svg+xml",
            // 文档类型
            "application/pdf",
            "application/msword",  // doc
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document", // docx
            "application/vnd.ms-excel", // xls
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", // xlsx
            "application/vnd.ms-powerpoint", // ppt
            "application/vnd.openxmlformats-officedocument.presentationml.presentation", // pptx
            "application/vnd.oasis.opendocument.text", // odt
            "application/vnd.oasis.opendocument.spreadsheet", // ods
            "application/vnd.oasis.opendocument.presentation", // odp
            // 文本类型
            "text/plain", "text/html", "text/css", "text/javascript",
            "text/csv", "text/xml", "application/json", "application/xml",
            "text/markdown", "text/x-markdown",
            // 压缩文件类型
            "application/zip", "application/x-rar-compressed", "application/x-7z-compressed",
            "application/x-tar", "application/gzip", "application/x-bzip2",
            // 音频类型
            "audio/mpeg", "audio/wav", "audio/ogg", "audio/midi", "audio/x-midi",
            "audio/aac", "audio/webm", "audio/x-m4a",
            // 视频类型
            "video/mp4", "video/mpeg", "video/quicktime", "video/x-msvideo",
            "video/x-ms-wmv", "video/webm", "video/3gpp",
            // 其他常用类型
            "application/x-httpd-php", // PHP文件
            "application/sql", // SQL文件
            "application/x-yaml", // YAML文件
            "application/x-python", // Python文件
            "application/java-archive", // JAR文件
            "application/x-java-source" // Java源文件
    ));

    /**
     * 允许上传的文件扩展名
     */
    private static final Set<String> ALLOWED_EXTENSIONS = new HashSet<>(Arrays.asList(
            // 图片扩展名
            "jpg", "jpeg", "png", "gif", "bmp", "webp",
            // 文档扩展名
            "doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf",
            // 文本扩展名
            "txt", "log", "ini", "json", "xml", "html", "css", "js",
            // 压缩包扩展名
            "zip", "rar", "7z", "tar", "gz",
            // 音视频扩展名
            "mp4", "mp3", "wav"
    ));

    /**
     * 最大文件大小限制: 1GB
     */
    private static final long MAX_FILE_SIZE = 1024L * 1024L * 1024L; // 1GB = 1024MB = 1024 * 1024 * 1024 bytes

    /**
     * 可预览的文件类型集合
     */
    private static final Set<String> PREVIEWABLE_CONTENT_TYPES = new HashSet<>(Arrays.asList(
        // 图片类型
        "image/jpeg", "image/png", "image/gif", "image/bmp", "image/webp", 
        "image/tiff", "image/svg+xml",
        
        // 文档类型
        "application/pdf",
        "application/msword", // doc
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document", // docx
        "application/vnd.ms-excel", // xls
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", // xlsx
        "application/vnd.ms-powerpoint", // ppt
        "application/vnd.openxmlformats-officedocument.presentationml.presentation", // pptx
        
        // 文本类型
        "text/plain", "text/html", "text/css", "text/javascript",
        "text/csv", "text/xml", "application/json", "application/xml",
        "text/markdown", "text/x-markdown"
    ));

    /**
     * 可预览的文件扩展名集合
     */
    private static final Set<String> PREVIEWABLE_EXTENSIONS = new HashSet<>(Arrays.asList(
        // 图片
        "jpg", "jpeg", "png", "gif", "bmp", "webp", "svg",
        // 文档
        "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx",
        // 文本
        "txt", "html", "css", "js", "json", "xml", "md", "log", "ini"
    ));

    @Resource
    MinIoUtil minIoUtil;  // MinIO操作工具类

    @Resource
    private UserAttachmentService userAttachmentService;  // 用户附件服务

    //@Value("${minio.bucketName}")
    private static String bucketName = "questionbank";  // MinIO存储桶名称

    @PostConstruct
    public void init() {
        // 初始化时检查并创建存储桶
        if (!minIoUtil.bucketExists(bucketName)) {
            log.info("Storage bucket {} does not exist, creating...", bucketName);
            if (minIoUtil.createBucket(bucketName, null)) {
                log.info("Storage bucket {} created successfully", bucketName);
            } else {
                log.error("Failed to create storage bucket {}", bucketName);
            }
        }
    }

    /**
     * 获取用户文件完整路径
     * @param userId 用户ID
     * @param objectName 文件名
     * @return 完整路径
     */
    private String getUserFilePath(Long userId, String objectName) {
        return String.format("user_%d/%s", userId, objectName);
    }

    /**
     * 上传文件
     * @param file 文件
     * @param path 存储路径(可选)
     * @param userId 用户ID
     * @return 上传结果
     */
    @PostMapping("/upload")
    @ApiOperation("上传文件")
    public ResponseEntity<Map<String, Object>> upload(
            @RequestParam("file") MultipartFile file,
            @RequestParam(value = "path", required = false) String path,
            @RequestParam("userId") Long userId,
            @RequestParam(value = "isFolder", required = false, defaultValue = "false") boolean isFolder) {
        try {
            // 确保存储桶存在
            if (!minIoUtil.bucketExists(bucketName)) {
                if (!minIoUtil.createBucket(bucketName, null)) {
                    return ResponseEntity.badRequest()
                            .body(Map.of("error", "存储服务初始化失败"));
                }
            }

            // 处理文件名，替换特殊字符
            String originalFilename = file.getOriginalFilename();
            if (originalFilename == null || originalFilename.isEmpty()) {
                return ResponseEntity.badRequest()
                        .body(Map.of("error", "文件名不能为空"));
            }

            // 如果是文件夹，跳过文件类型验证
            if (!isFolder) {
                // 验证文件类型
                String contentType = file.getContentType();
                String extension = getFileExtension(originalFilename);
                
                if (!isAllowedFile(contentType, extension)) {
                    return ResponseEntity.badRequest()
                            .body(Map.of("error", "不支持的文件类型，仅支持: " + 
                                String.join(", ", ALLOWED_EXTENSIONS)));
                }

                // 验证文件大小
                if (file.getSize() > MAX_FILE_SIZE) {
                    return ResponseEntity.badRequest()
                            .body(Map.of("error", "文件大小不能超过1GB"));
                }
            }

            // 处理文件名，替换特殊字符
            // String safeFileName = originalFilename.replaceAll("[\\s\\(\\)（）]+", "_")
            //         .replaceAll("[^a-zA-Z0-9._\\-/]+", "");

            // 构建文件路径，添加用户ID作为根目录
            String fileName = (path != null && !path.isEmpty()) ?
                    (path + "/" + originalFilename) : originalFilename;
            String objectName = getUserFilePath(userId, fileName);
            if (isFolder) {
                objectName += "/"; // 文件夹路径以/结尾
            }
            String fileUrl = "/" + bucketName + "/" + objectName;

            // 检查文件是否已存在
            UserAttachment existingFile = userAttachmentService.getByUrlAndUserId(fileUrl, userId);

            if (existingFile != null) {
                // 文件已存在，更新文件
                existingFile.setFileSize(isFolder ? 0 : (int)file.getSize());
                existingFile.setUploadTime(new Date());
                userAttachmentService.updateById(existingFile);

                var response = minIoUtil.uploadFile(bucketName, objectName, file);

                Map<String, Object> result = new HashMap<>();
                result.put("url", existingFile.getAttachmentUrl());
                result.put("etag", response.etag());
                result.put("attachmentId", existingFile.getAttachmentId());
                result.put("order", existingFile.getAttachmentOrder());
                result.put("fileType", isFolder ? "folder" : getFileExtension(originalFilename));
                result.put("updated", true);

                return ResponseEntity.ok(result);
            } else {
                // 文件不存在，新建文件
                UserAttachment attachment = new UserAttachment();
                attachment.setUserId(userId);
                attachment.setAttachmentName(originalFilename);
                attachment.setAttachmentUrl(fileUrl);
                attachment.setAttachmentFileType(isFolder ? "folder" : getFileExtension(originalFilename));
                attachment.setFileSize(isFolder ? 0 : (int)file.getSize());
                attachment.setUploadTime(new Date());

                Float maxOrder = userAttachmentService.getMaxOrderByUserId(userId);
                attachment.setAttachmentOrder(maxOrder != null ? maxOrder + 1 : 1.0f);

                // 保存到数据库
                userAttachmentService.save(attachment);

                try {
                    // 再上传到MinIO
                    var response = minIoUtil.uploadFile(bucketName, objectName, file);

                    Map<String, Object> result = new HashMap<>();
                    result.put("url", attachment.getAttachmentUrl());
                    result.put("etag", response.etag());
                    result.put("attachmentId", attachment.getAttachmentId());
                    result.put("order", attachment.getAttachmentOrder());
                    result.put("fileType", isFolder ? "folder" : getFileExtension(originalFilename));
                    result.put("updated", false);

                    return ResponseEntity.ok(result);
                } catch (Exception e) {
                    userAttachmentService.removeById(attachment.getAttachmentId());
                    throw e;
                }
            }

        } catch (Exception e) {
            log.error("文件上传失败", e);
            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
        }
    }

    /**
     * 获取文件扩展名
     * @param filename 文件名
     * @return 扩展名(小写)
     */
    private String getFileExtension(String filename) {
        return Optional.ofNullable(filename)
                .filter(f -> f.contains("."))
                .map(f -> f.substring(filename.lastIndexOf(".") + 1).toLowerCase())
                .orElse("");
    }

    /**
     * 检查文件类型是否允许
     * @param contentType 文件Content-Type
     * @param extension 文件扩展名
     * @return 是否允许
     */
    private boolean isAllowedFile(String contentType, String extension) {
        return ALLOWED_CONTENT_TYPES.contains(contentType) ||
                ALLOWED_EXTENSIONS.contains(extension);
    }

    /**
     * 下载文件
     * @param attachmentId 附件ID
     * @param userId 用户ID
     * @return 文件流
     */
    @GetMapping("/download")
    @ApiOperation("下载文件")
    public ResponseEntity<?> download(
            @RequestParam("attachmentId") Long attachmentId,
            @RequestParam("userId") Long userId) {
        try {
            UserAttachment attachment = userAttachmentService.getByIdAndUserId(attachmentId, userId);
            if (attachment == null) {
                return ResponseEntity.badRequest().body(Map.of("error", "文件不存在或无权访问"));
            }

            String filePath = attachment.getAttachmentUrl().substring(bucketName.length() + 2);
            StatObjectResponse stat = minIoUtil.getFileInfo(bucketName, filePath);
            GetObjectResponse response = minIoUtil.getFileStream(bucketName, filePath);

            return ResponseEntity.ok()
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .contentLength(stat.size())
                    .header(HttpHeaders.CONTENT_DISPOSITION,
                            "attachment; filename=\"" + attachment.getAttachmentName() + "\"")
                    .body(new InputStreamResource(response));

        } catch (Exception e) {
            log.error("文件下载失败", e);
            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
        }
    }

    /**
     * 删除文件
     * @param attachmentId 附件ID
     * @param userId 用户ID
     * @return 删除结果
     */
    @DeleteMapping("/delete")
    @ApiOperation("删除文件")
    public ResponseEntity<?> delete(
            @RequestParam("attachmentId") Long attachmentId,
            @RequestParam("userId") Long userId) {
        try {
            // 验证用户权限
            UserAttachment attachment = userAttachmentService.getByIdAndUserId(attachmentId, userId);
            if (attachment == null) {
                return ResponseEntity.badRequest().body(Map.of("error", "文件不存在或无权删除"));
            }

            // 从URL中提取文件路径
            String filePath = attachment.getAttachmentUrl()
                    .replace("/" + bucketName + "/", "");

            // 验证文件是否在用户目录下
            String userPrefix = String.format("user_%d/", userId);
            if (!filePath.startsWith(userPrefix)) {
                return ResponseEntity.badRequest().body(Map.of("error", "无权删除该文件"));
            }

            // 删除MinIO中的文件
            minIoUtil.deleteFile(bucketName, filePath);
            
            // 删除数据库记录
            userAttachmentService.removeById(attachmentId);

            return ResponseEntity.ok().build();

        } catch (Exception e) {
            log.error("删除文件失败: attachmentId={}, userId={}", attachmentId, userId, e);
            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
        }
    }

    /**
     * 获取文件列表
     * @param prefix 路径前缀(可选)
     * @param userId 用户ID
     * @return 文件列表
     */
    @GetMapping("/list")
    @ApiOperation("获取文件列表")
    public ResponseEntity<List<Map<String, Object>>> listFiles(
            @RequestParam(value = "prefix", required = false, defaultValue = "") String prefix,
            @RequestParam("userId") Long userId) {
        try {
            // 确保存储桶存在
            if (!minIoUtil.bucketExists(bucketName)) {
                if (!minIoUtil.createBucket(bucketName, null)) {
                    return ResponseEntity.badRequest()
                            .body(List.of(Map.of("error", "存储服务初始化失败")));
                }
            }

            // 添加用户ID到前缀
//            String userPrefix = String.format("user_%d/", userId);
//            if (!prefix.isEmpty()) {
//                userPrefix += prefix;
//            }
            final String userPrefix = !prefix.isEmpty() ?
                    String.format("user_%d/%s", userId, prefix) :
                    String.format("user_%d/", userId);

            // 获取MinIO中的文件列表
            List<MinIoUtil.FileItemDTO> minioFiles = minIoUtil.listFiles(bucketName, userPrefix, false);

            // 从数据库获取用户文件列表
            List<UserAttachment> attachments = userAttachmentService.listByUserId(userId);

            // 合并数据库和MinIO的文件信息
            List<Map<String, Object>> files = attachments.stream()
                    .filter(attachment -> {
                        String filePath = attachment.getAttachmentUrl().substring(bucketName.length() + 2);
                        return userPrefix.isEmpty() || filePath.startsWith(userPrefix);
                    })
                    .map(attachment -> {
                        try {
                            String filePath = attachment.getAttachmentUrl().substring(bucketName.length() + 2);

                            // 查找对应的MinIO文件信息
                            MinIoUtil.FileItemDTO minioFile = minioFiles.stream()
                                    .filter(f -> f.getPath().equals(filePath))
                                    .findFirst()
                                    .orElse(null);

                            if (minioFile == null) {
                                log.warn("文件在MinIO中不存在: {}", filePath);
                                return null;
                            }

                            Map<String, Object> fileInfo = new HashMap<>();
                            fileInfo.put("attachmentId", attachment.getAttachmentId());
                            fileInfo.put("name", attachment.getAttachmentName());
                            fileInfo.put("path", filePath);
                            fileInfo.put("url", attachment.getAttachmentUrl());
                            fileInfo.put("size", attachment.getFileSize());
                            fileInfo.put("uploadTime", attachment.getUploadTime());
                            fileInfo.put("directory", "folder".equals(attachment.getAttachmentFileType()));
                            fileInfo.put("fileType", attachment.getAttachmentFileType());
                            fileInfo.put("order", attachment.getAttachmentOrder());
                            fileInfo.put("etag", minioFile.getEtag());

                            return fileInfo;
                        } catch (Exception e) {
                            log.error("获取文件信息失败: {}", attachment.getAttachmentUrl(), e);
                            return null;
                        }
                    })
                    .filter(item -> item != null)
                    .sorted((a, b) -> Float.compare((Float)a.get("order"), (Float)b.get("order")))
                    .collect(Collectors.toList());

            return ResponseEntity.ok(files);

        } catch (Exception e) {
            log.error("获取文件列表失败", e);
            return ResponseEntity.badRequest().build();
        }
    }

    /**
     * 创建文件夹
     * @param path 文件夹路径
     * @param userId 用户ID
     * @return 创建结果
     */
    @PostMapping("/mkdir")
    @ApiOperation("创建文件夹")
    public ResponseEntity<?> mkdir(
            @RequestParam("path") String path,
            @RequestParam("userId") Long userId) {
        try {
            // 获取文件夹名称
            String folderName = path;
            if (path.contains("/")) {
                folderName = path.substring(path.lastIndexOf('/') + 1);
            }

            // 添加用户ID到路径
            String fullPath = getUserFilePath(userId, path);

            // 创建文件夹 - 使用文件夹名称而不是完整路径
            minIoUtil.uploadFile(bucketName, fullPath + "/", new EmptyMultipartFile(folderName));

            UserAttachment folder = new UserAttachment();
            folder.setUserId(userId);
            folder.setAttachmentName(folderName); // 使用文件夹名称
            folder.setAttachmentUrl("/" + bucketName + "/" + fullPath + "/");
            folder.setFileSize(0);
            folder.setAttachmentFileType("folder");
            folder.setUploadTime(new Date());

            Float maxOrder = userAttachmentService.getMaxOrderByUserId(userId);
            folder.setAttachmentOrder(maxOrder != null ? maxOrder + 1 : 1.0f);

            userAttachmentService.save(folder);

            return ResponseEntity.ok().build();

        } catch (Exception e) {
            log.error("创建文件夹失败", e);
            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
        }
    }

    /**
     * 判断文件是否可以预览
     */
    private boolean isPreviewable(String contentType, String extension) {
        // 检查Content-Type
        if (contentType != null && PREVIEWABLE_CONTENT_TYPES.contains(contentType)) {
            return true;
        }
        
        // 检查文件扩展名
        return extension != null && PREVIEWABLE_EXTENSIONS.contains(extension.toLowerCase());
    }

    /**
     * 预览文件
     * @param attachmentId 附件ID
     * @param userId 用户ID
     * @return 文件流
     */
    @GetMapping("/preview")
    @ApiOperation("预览文件")
    public ResponseEntity<?> preview(
            @RequestParam("attachmentId") Long attachmentId,
            @RequestParam("userId") Long userId) {
        try {
            // 验证用户权限
            UserAttachment attachment = userAttachmentService.getByIdAndUserId(attachmentId, userId);
            if (attachment == null) {
                return ResponseEntity.badRequest().body(Map.of("error", "文件不存在或无权访问"));
            }

            // 从URL中提取文件路径
            String filePath = attachment.getAttachmentUrl()
                    .replace("/" + bucketName + "/", "");

            // 验证文件是否在用户目录下
            String userPrefix = String.format("user_%d/", userId);
            if (!filePath.startsWith(userPrefix)) {
                return ResponseEntity.badRequest().body(Map.of("error", "无权预览该文件"));
            }

            String extension = getFileExtension(attachment.getAttachmentName());

            // 获取文件信息
            StatObjectResponse stat = minIoUtil.getFileInfo(bucketName, filePath);
            String contentType = stat.contentType();

            // 检查文件是否可预览
            if (!isPreviewable(contentType, extension)) {
                return ResponseEntity.badRequest()
                        .body(Map.of("error", "不支持该文件类型的预览"));
            }

            // 获取文件流
            GetObjectResponse response = minIoUtil.getFileStream(bucketName, filePath);

            // 设置合适的Content-Type
            MediaType mediaType = MediaType.parseMediaType(
                contentType != null ? contentType : "application/octet-stream");

            // 返回文件流
            return ResponseEntity.ok()
                    .contentType(mediaType)
                    .contentLength(stat.size())
                    .header(HttpHeaders.CONTENT_DISPOSITION,
                            "inline; filename=\"" + attachment.getAttachmentName() + "\"")
                    .header(HttpHeaders.CACHE_CONTROL, "no-cache")
                    .body(new InputStreamResource(response));

        } catch (Exception e) {
            log.error("文件预览失败: attachmentId={}, userId={}", attachmentId, userId, e);
            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
        }
    }
}

